# Subversion: $Id:$
# $HeadURL:$
#
# Part of: daemon-kit at http://code.google.com/p/daemon-kit/
# Author: Andrew Ault
# Created: 2009-05-17
#
# This file is part of Daemon-kit.
#
# Daemon-kit is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Daemon-kit is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Daemon-kit.  If not, see <http://www.gnu.org/licenses/>.

package PerlDaemonKitControl;
use strict;
use warnings;

sub new {
	my $class = shift;              # $class will contain the package name "PerlDaemonKitControl"
	my $self = bless {}, $class;    #
	$self->{daemonCommand}       = undef;       # command that will be run as a daemon
	$self->{daemonCommandPath}   = undef;       # fully qualified path to the command on this system
	$self->{daemonCommandParms}  = undef;       # command parameters for $command
	$self->{daemonCommandName}   = undef;       # human-fiendly name of the program
	$self->{_daemonCommandWPath} = undef;       # command with path
	$self->{_daemonCommandFull}  = undef;       # command with path and parameters (full command)
	$self->{_PIDFileName}        = undef;       # PID file with full path
	$self->{_UserName}           = `whoami`;    # user running this
	chomp $self->{_UserName};
	$self->{_argument} = undef;                 # command line argument

	return $self;
}

# public functions
sub daemonCommand {
	my $self = shift;
	if (@_) { $self->{daemonCommand} = shift }
	return $self->{daemonCommand};
}

sub daemonCommandPath {
	my $self = shift;
	if (@_) {
		# append a trailing slash, if one was not supplied
		my $commandPath = shift;
		if ( substr($commandPath, -1) eq "/" ){
			$self->{daemonCommandPath} = $commandPath;
		} else {
			$self->{daemonCommandPath} = $commandPath . "/";
		}
	}
	return $self->{daemonCommandPath};
}

sub daemonCommandParms {
	my $self = shift;
	if (@_) { $self->{daemonCommandParms} = shift }
	return $self->{daemonCommandParms};
}

sub daemonCommandName {
	my $self = shift;
	if (@_) { $self->{daemonCommandName} = shift }
	return $self->{daemonCommandName};
}

sub preFlight {
	my $self = shift;

	# check that Customization block has been set up
	if (    $self->{daemonCommand} eq ""
		 && $self->{daemonCommandPath}  eq ""
		 && $self->{daemonCommandName}  eq "" )
	{
		$self->errorExit('noSetup');
	}

	# check command path existence
	if ( !-e $self->{daemonCommandPath} ) {
		$self->errorExit('noPath');
	}

	# check for the daemon program
	if ( !-e $self->{daemonCommandPath} . $self->{daemonCommand} ) {
		$self->errorExit('noProg');
	}

	# if not run as "root" then error out
	if ( $self->{_UserName} ne "root" ) {
		$self->errorExit('rootReq');
	}

	# IF > or < 1 argument, error
	if ( @ARGV > 1 || @ARGV < 1 ) {
		$self->errorExit('argReq');
	}
}

sub execute {
	my $self     = shift;
	my $argument = $ARGV[0];
	$self->calc();
	$self->preFlight();

	# go do the voodoo that we do so well
	if ( $argument eq 'start' ) {
		print "daemonStart"

		  #daemonStart();
	} elsif ( $argument eq 'restart' ) {
		print "daemonRestart"

		  #daemonRestart();
	} elsif ( $argument eq 'stop' ) {
		print "daemonStop"

		  #daemonStop();
	} elsif ( $argument eq 'pid' ) {
		print "daemonPid"

		  #daemonPid();
	} elsif ( $argument eq 'help' || $argument eq '--help' || $argument eq '-help' || $argument eq '-h' ) {
		print "displayUsage"

		  #displayUsage();
	} else {
		$self->errorExit('unkArg');
	}
	exit(0);
}

# private functions
sub calc {
	my $self = shift;
	$self->{_daemonCommandWPath} = $self->{daemonCommandPath} . $self->{daemonCommand};
	$self->{_daemonCommandFull}  = $self->{_daemonCommandWPath} . " " . $self->{daemonCommandParms};
	$self->{_PIDFileName}        = "/var/run/" . $self->{daemonCommand} . ".pid";
}


# start daemon
sub daemonStart {
	my $self = shift;
	# error out if the dameon is already running
	if ( -e $self->{_PIDFileName} ) {
		$self->errorExit('alreadyRunning');
	}
	print "<<<Starting>>> the '$self->{_daemonCommandWPath}' daemon.\n";
	my $result = system ( $self->{_daemonCommandFull} );
	# TODO: add check to see if program is running and error out if it is not
	$self->pidCreate();
	return;
}

# restart daemon
sub daemonRestart {
	my $self = shift;
	my $result;
	# whoa, can't restart a program that is not running
	if ( !-e $self->{_PIDFileName} ) {
		$self->errorExit('notRunning');
	}
	$result = system ( "pkill $self->{daemonCommand}" );
	$self->pidDelete();
	$result = system ( $self->{_daemonCommandFull} );
	$self->pidCreate();
	print "<<<Restarting>>> the '$self->{_daemonCommandWPath}' daemon.\n";
	return;
}

# stop daemon
sub daemonStop {
	my $self = shift;
	# can't stop a program that is not running
	if ( !-e $self->{_PIDFileName} ) {
		$self->errorExit('notRunning');
	}
	my $result = system ( "pkill $self->{daemonCommand}" );
	$self->pidDelete();
	print "<<<Stopping>>> the '$self->{_daemonCommandWPath}' daemon.\n";
	return;
}

# show pid
sub daemonPid {
	my $self = shift;
	return;
}

# create pid file
sub pidCreate {
	my $self = shift;
	#my $pid = qx( "pgrep $command" );
	#chomp $pid;
	my $pid = $self->{daemonCommand};

	open my $fhPid, ">>", $self->{_PIDFileName} or $self->errorExit('errPid');
	if ( ! $fhPid ) {
		errorExit('errPid');
	}
	print $fhPid "$pid\n";
	close $fhPid;
	return;
}

# delete pid file
sub pidDelete {
	my $self = shift;
	eval { unlink $self->{_PIDFileName} }; $self->errorExit('errPidDelete') if $@;
	return;
}

# Display program usage help
sub displayUsage {
	my $self = shift;
	print "\n$self->{daemonCommandName} manager\n";
	print "\n$0 is the process management utility for the '$self->{_daemonCommandWPath}' daemon.\n\n";
	print "usage:\t\t$0 start|stop|restart|pid|help\n\n";
	print "where:\t\tstart\t\tStarts the $self->{daemonCommand} daemon.\n";
	print "\t\trestart\t\tStops and starts the $self->{daemonCommand} daemon.\n";
	print "\t\tstop\t\tStops the $self->{daemonCommand} daemon.\n";
	print "\t\tpid\t\tDisplays the $self->{daemonCommand} daemon's PID.\n";
	print "\t\thelp\t\tDisplays this help.\n";
	print "\nexample:\t$0 start\n\n";
	print "project:\thttp://code.google.com/p/daemon-kit/\n\n";
	return;
}

# Display an error and exit
sub errorExit {
	my ( $self, $errType ) = @_;

	my $progArg = "";
	if ( defined $self->{argument} ) {
		$progArg = $self->{argument};
	}

	my %errTypes = (
					 'noSetup' => {
									'error'        => 'PerlDaemonKitControl.pm has not been initialized adequately',
									'explaination' => "See documentation for implementation instructions.",
					 },
					 'noPath' => {
								   'error'        => 'command path doesn\'t exist',
								   'explaination' => "'$self->{_daemonCommandWPath}' does not exist. Installation error.",
					 },
					 'noProg' => {
								   'error'        => 'daemon program doesn\'t exist',
								   'explaination' => "'$self->{daemonCommandPath}' does not exist. Installation error.",
					 },
					 'argReq' => {
								   'error'        => 'argument required',
								   'explaination' => "$0 requires a command line argument.",
					 },
					 'rootReq' => {
									'error'        => 'root privliges required',
									'explaination' => "$0 must be run as 'root'. You are '$self->{_UserName}'.",
					 },
					 'unkArg' => {
								   'error'        => 'unknown argument',
								   'explaination' => "Don't know what '$progArg' means.",
					 },
					 'alreadyRunning' => {
										   'error'        => 'daemon already running',
										   'explaination' => "Only one '$self->{daemonCommand}' can run at a time.",
					 },
					 'notRunning' => {
									   'error'        => 'daemon not running',
									   'explaination' => "Can't stop or restart that which is not running.",
					 },
					 'errPid' => {
								   'error'        => "can't create PID file",
								   'explaination' => "Error creating the '$self->{_PIDFileName}' PID file.",
					 },
					 'errPidDelete' => {
										 'error'        => "can't delete PID file",
										 'explaination' => "Error deleting the '$self->{_PIDFileName}' PID file.",
					 },
	);

	print STDERR "ERROR:\t\t**" . $errTypes{$errType}{'error'} . "**\n";
	print STDERR "\t\t" . $errTypes{$errType}{'explaination'} . "\n";
	$self->displayUsage();
	exit(1);
}

1;
